/*
 * @brief LPC8xx UART driver
 *
 * @note
 * 基于官方LPCOpen中uart_8xx驱动程序改写
 */

#ifndef __UART_8XX_H_
#define __UART_8XX_H_

#ifdef __cplusplus
extern "C" {
#endif

//#include "ring_buffer.h"

/** @defgroup UART_8XX CHIP: LPC8xx UART Driver
 * @ingroup CHIP_8XX_Drivers
 * @{
 */

#if 0
/**
 * @brief UART register block structure
 */
typedef struct {
    __IO uint32_t  CFG;             /*!< Configuration register */
    __IO uint32_t  CTRL;            /*!< Control register */
    __IO uint32_t  STAT;            /*!< Status register */
    __IO uint32_t  INTENSET;        /*!< Interrupt Enable read and set register */
    __O  uint32_t  INTENCLR;        /*!< Interrupt Enable clear register */
    __I  uint32_t  RXDATA;          /*!< Receive Data register */
    __I  uint32_t  RXDATA_STAT;     /*!< Receive Data with status register */
    __IO uint32_t  TXDATA;          /*!< Transmit data register */
    __IO uint32_t  BRG;             /*!< Baud Rate Generator register */
    __IO uint32_t  INTSTAT;         /*!< Interrupt status register */
    __IO uint32_t  OSR;             /*!< Oversampling Selection regiser */
    __IO uint32_t  ADDR;            /*!< Address register for automatic address matching */
} LPC_USART_T;
#endif

/* 兼容与设备头文件的定义 */
#define LPC_USART_T LPC_USART_TypeDef

/**
 * @brief UART CFG register definitions
 */
#define UART_CFG_ENABLE         (0x01 << 0)
#define UART_CFG_DATALEN_7      (0x00 << 2)     /*!< UART 7 bit length mode */
#define UART_CFG_DATALEN_8      (0x01 << 2)     /*!< UART 8 bit length mode */
#define UART_CFG_DATALEN_9      (0x02 << 2)     /*!< UART 9 bit length mode */
#define UART_CFG_PARITY_NONE    (0x00 << 4)     /*!< No parity */
#define UART_CFG_PARITY_EVEN    (0x02 << 4)     /*!< Even parity */
#define UART_CFG_PARITY_ODD     (0x03 << 4)     /*!< Odd parity */
#define UART_CFG_STOPLEN_1      (0x00 << 6)     /*!< UART One Stop Bit Select */
#define UART_CFG_STOPLEN_2      (0x01 << 6)     /*!< UART Two Stop Bits Select */
#define UART_CFG_CTSEN          (0x01 << 9)     /*!< CTS enable bit */
#define UART_CFG_SYNCEN         (0x01 << 11)    /*!< Synchronous mode enable bit */
#define UART_CFG_CLKPOL         (0x01 << 12)    /*!< Un_RXD rising edge sample enable bit */
#define UART_CFG_SYNCMST        (0x01 << 14)    /*!< Select master mode (synchronous mode) enable bit */
#define UART_CFG_LOOP           (0x01 << 15)    /*!< Loopback mode enable bit */

#ifdef CHIP_LPC82X /* LPC82X specific bits */
#define UART_CFG_OETA           (0x01 << 18)    /*!< Output Enable Turnaround time for RS485 */
#define UART_CFG_AUTOADDR       (0x01 << 19)    /*!< Automatic address matching enable */
#define UART_CFG_OESEL          (0x01 << 20)    /*!< Output enable select */
#define UART_CFG_OEPOL          (0x01 << 21)    /*!< Output enable polarity */
#define UART_CFG_RXPOL          (0x01 << 22)    /*!< Receive data polarity */
#define UART_CFG_TXPOL          (0x01 << 22)    /*!< Transmit data polarity */
#define UART_CFG_RESERVED       ((1<<1)|(1<<7)|(1<<8)|(1<<10)|(1<<13)|(3 << 16)|(0xffu<<24))
#else
#define UART_CFG_RESERVED       ((1<<1)|(1<<7)|(1<<8)|(1<<10)|(1<<13)|(0xffffu<<16))
#endif

/**
 * @brief UART CTRL register definitions
 */
#define UART_CTRL_TXBRKEN       (0x01 << 1)     /*!< Continuous break enable bit */
#define UART_CTRL_ADDRDET       (0x01 << 2)     /*!< Address detect mode enable bit */
#define UART_CTRL_TXDIS         (0x01 << 6)     /*!< Transmit disable bit */
#define UART_CTRL_CC            (0x01 << 8)     /*!< Continuous Clock mode enable bit */
#define UART_CTRL_CLRCC         (0x01 << 9)     /*!< Clear Continuous Clock bit */
#ifdef CHIP_LPC82X
#define UART_CTRL_AUTOBAUD      (1 << 16)       /*!< Enable UART Autobaud */
#define UART_CTRL_RESERVED      (0xFFFEFCB9U)
#else
#define UART_CTRL_RESERVED      (1|(7<<3)|(1<<7)|0xfffffc00u)
#endif

/**
 * @brief UART STAT register definitions
 */
#define UART_STAT_RXRDY         (0x01 << 0)         /*!< Receiver ready */
#define UART_STAT_RXIDLE        (0x01 << 1)         /*!< Receiver idle */
#define UART_STAT_TXRDY         (0x01 << 2)         /*!< Transmitter ready for data */
#define UART_STAT_TXIDLE        (0x01 << 3)         /*!< Transmitter idle */
#define UART_STAT_CTS           (0x01 << 4)         /*!< Status of CTS signal */
#define UART_STAT_DELTACTS      (0x01 << 5)         /*!< Change in CTS state */
#define UART_STAT_TXDISINT      (0x01 << 6)         /*!< Transmitter disabled */
#define UART_STAT_OVERRUNINT    (0x01 << 8)         /*!< Overrun Error interrupt flag. */
#define UART_STAT_RXBRK         (0x01 << 10)        /*!< Received break */
#define UART_STAT_DELTARXBRK    (0x01 << 11)        /*!< Change in receive break detection */
#define UART_STAT_START         (0x01 << 12)        /*!< Start detected */
#define UART_STAT_FRM_ERRINT    (0x01 << 13)        /*!< Framing Error interrupt flag */
#define UART_STAT_PAR_ERRINT    (0x01 << 14)        /*!< Parity Error interrupt flag */
#define UART_STAT_RXNOISEINT    (0x01 << 15)        /*!< Received Noise interrupt flag */
#ifdef CHIP_LPC82X
#define UART_STAT_ABERR         (0x01 << 16)        /*!< Auto baud error */
#define UART_STAT_RESERVED      ((1<<7)|(1<<9)|(0xFFFEU<<16))
#else
#define UART_STAT_RESERVED      ((1<<7)|(1<<9)|(0xffffu<<16))
#endif

/**
 * @brief UART INTENSET/INTENCLR register definitions
 */
#define UART_INTEN_RXRDY        (0x01 << 0)         /*!< Receive Ready interrupt */
#define UART_INTEN_TXRDY        (0x01 << 2)         /*!< Transmit Ready interrupt */
#define UART_INTEN_DELTACTS     (0x01 << 5)         /*!< Change in CTS state interrupt */
#define UART_INTEN_TXDIS        (0x01 << 6)         /*!< Transmitter disable interrupt */
#define UART_INTEN_OVERRUN      (0x01 << 8)         /*!< Overrun error interrupt */
#define UART_INTEN_DELTARXBRK   (0x01 << 11)        /*!< Change in receiver break detection interrupt */
#define UART_INTEN_START        (0x01 << 12)        /*!< Start detect interrupt */
#define UART_INTEN_FRAMERR      (0x01 << 13)        /*!< Frame error interrupt */
#define UART_INTEN_PARITYERR    (0x01 << 14)        /*!< Parity error interrupt */
#define UART_INTEN_RXNOISE      (0x01 << 15)        /*!< Received noise interrupt */
#ifdef CHIP_LPC82X
#define UART_INTEN_TXIDLE       (0x01 << 3)         /*!< TX Idle enable/clear */
#define UART_INTEN_ABERR        (0x01 << 16)        /*!< Auto baud error */
#define UART_INTEN_RESERVED     ((1<<1)|(1<<4)|(1<<7)|(3<<9)|(0xfffeu<<16))
#define UART_INTSTAT_RESERVED   ((1<<1)|(1<<4)|(1<<7)|(3<<9)|(0xfffeu<<16))
#else
#define UART_INTEN_RESERVED     ((1<<1)|(3<<3)|(1<<7)|(3<<9)|(0xffffu<<16))
#define UART_INTSTAT_RESERVED   ((1<<1)|(3<<3)|(1<<7)|(3<<9)|(0xffffu<<16))
#endif

/**
 * @brief   Enable the UART
 * @param   pUART       : Pointer to selected UARTx peripheral
 * @return  Nothing
 */
static inline void Chip_UART_Enable(LPC_USART_T *pUART)
{
    pUART->CFG = UART_CFG_ENABLE | (pUART->CFG & ~UART_CFG_RESERVED);
}

/**
 * @brief   Disable the UART
 * @param   pUART   : Pointer to selected UARTx peripheral
 * @return  Nothing
 */
static inline void Chip_UART_Disable(LPC_USART_T *pUART)
{
    pUART->CFG &= ~(UART_CFG_RESERVED | UART_CFG_ENABLE);
}

/**
 * @brief   Enable transmission on UART TxD pin
 * @param   pUART   : Pointer to selected pUART peripheral
 * @return Nothing
 */
static inline void Chip_UART_TXEnable(LPC_USART_T *pUART)
{
    pUART->CTRL &= ~(UART_CTRL_RESERVED | UART_CTRL_TXDIS);
}

/**
 * @brief   Disable transmission on UART TxD pin
 * @param   pUART   : Pointer to selected pUART peripheral
 * @return Nothing
 */
static inline void Chip_UART_TXDisable(LPC_USART_T *pUART)
{
    pUART->CTRL = UART_CTRL_TXDIS | (pUART->CTRL & ~UART_CTRL_RESERVED);
}

/**
 * @brief   Transmit a single data byte through the UART peripheral
 * @param   pUART   : Pointer to selected UART peripheral
 * @param   data    : Byte to transmit
 * @return  Nothing
 * @note    This function attempts to place a byte into the UART transmit
 *          holding register regard regardless of UART state.
 */
static inline void Chip_UART_SendByte(LPC_USART_T *pUART, uint8_t data)
{
    pUART->TXDATA = (uint32_t) data;
}

/**
 * @brief   Read a single byte data from the UART peripheral
 * @param   pUART   : Pointer to selected UART peripheral
 * @return  A single byte of data read
 * @note    This function reads a byte from the UART receive FIFO or
 *          receive hold register regard regardless of UART state. The
 *          FIFO status should be read first prior to using this function
 */
static inline uint32_t Chip_UART_ReadByte(LPC_USART_T *pUART)
{
    /* Strip off undefined reserved bits, keep 9 lower bits */
    return (uint32_t) (pUART->RXDATA & 0x000001FF);
}

/**
 * @brief   Enable UART interrupts
 * @param   pUART   : Pointer to selected UART peripheral
 * @param   intMask : OR'ed Interrupts to enable
 * @return  Nothing
 * @note    Use an OR'ed value of UART_INTEN_* definitions with this function
 *          to enable specific UART interrupts.
 */
static inline void Chip_UART_IntEnable(LPC_USART_T *pUART, uint32_t intMask)
{
    pUART->INTENSET = intMask;
}

/**
 * @brief   Disable UART interrupts
 * @param   pUART   : Pointer to selected UART peripheral
 * @param   intMask : OR'ed Interrupts to disable
 * @return  Nothing
 * @note    Use an OR'ed value of UART_INTEN_* definitions with this function
 *          to disable specific UART interrupts.
 */
static inline void Chip_UART_IntDisable(LPC_USART_T *pUART, uint32_t intMask)
{
    pUART->INTENCLR = intMask;
}

/**
 * @brief   Returns UART interrupts that are enabled
 * @param   pUART   : Pointer to selected UART peripheral
 * @return  Returns the enabled UART interrupts
 * @note    Use an OR'ed value of UART_INTEN_* definitions with this function
 *          to determine which interrupts are enabled. You can check
 *          for multiple enabled bits if needed.
 */
static inline uint32_t Chip_UART_GetIntsEnabled(LPC_USART_T *pUART)
{
    return (pUART->INTENSET & ~UART_INTEN_RESERVED);
}

/**
 * @brief   Get UART interrupt status
 * @param   pUART   : The base of UART peripheral on the chip
 * @return  The Interrupt status register of UART
 * @note    Multiple interrupts may be pending. Mask the return value
 *          with one or more UART_INTEN_* definitions to determine
 *          pending interrupts.
 */
static inline uint32_t Chip_UART_GetIntStatus(LPC_USART_T *pUART)
{
    return (pUART->INTSTAT & ~UART_INTSTAT_RESERVED);
}

/**
 * @brief   Configure data width, parity and stop bits
 * @param   pUART   : Pointer to selected pUART peripheral
 * @param   config  : UART configuration, OR'ed values of select UART_CFG_* defines
 * @return  Nothing
 * @note    Select OR'ed config options for the UART from the UART_CFG_PARITY_*,
 *          UART_CFG_STOPLEN_*, and UART_CFG_DATALEN_* definitions. For example,
 *          a configuration of 8 data bits, 1 stop bit, and even (enabled) parity would be
 *          (UART_CFG_DATALEN_8 | UART_CFG_STOPLEN_1 | UART_CFG_PARITY_EVEN). Will not
 *          alter other bits in the CFG register.
 */
static inline void Chip_UART_ConfigData(LPC_USART_T *pUART, uint32_t config)
{
    uint32_t reg;

    reg = pUART->CFG & ~((0x3 << 2) | (0x3 << 4) | (0x1 << 6) | UART_CFG_RESERVED);
    pUART->CFG = reg | config;
}

/**
 * @brief   Get the UART status register
 * @param   pUART   : Pointer to selected UARTx peripheral
 * @return  UART status register
 * @note    Multiple statuses may be pending. Mask the return value
 *          with one or more UART_STAT_* definitions to determine
 *          statuses.
 */
static inline uint32_t Chip_UART_GetStatus(LPC_USART_T *pUART)
{
    return (pUART->STAT & ~UART_STAT_RESERVED);
}

/**
 * @brief   Clear the UART status register
 * @param   pUART   : Pointer to selected UARTx peripheral
 * @param   stsMask : OR'ed statuses to disable
 * @return  Nothing
 * @note    Multiple interrupts may be pending. Mask the return value
 *          with one or more UART_INTEN_* definitions to determine
 *          pending interrupts.
 */
static inline void Chip_UART_ClearStatus(LPC_USART_T *pUART, uint32_t stsMask)
{
    pUART->STAT = stsMask;
}

/**
 * @brief   Set oversample value
 * @param   pUART       : Pointer to selected UARTx peripheral
 * @param   ovrVal      : Oversample value (can be from 5 to 16)
 * @return  Nothing
 * @note    The valid values for ovrVal is 5 to 16 (samples per bit)
 */
static inline void Chip_UART_SetOSR(LPC_USART_T *pUART, uint32_t ovrVal)
{
    pUART->OSR = ovrVal - 1;
}

/**
 * @brief   Set address for hardware address matching
 * @param   pUART       : Pointer to selected UARTx peripheral
 * @param   addr        : Address to compare (0x00 to 0xFF)
 * @return  Nothing
 * @note    The valid values for addr is 0x00 to 0xFF
 */
static inline void Chip_UART_SetAddr(LPC_USART_T *pUART, uint32_t addr)
{
    pUART->ADDR = addr;
}

/**
 * @brief   Transmit a byte array through the UART peripheral (non-blocking)
 * @param   pUART       : Pointer to selected UART peripheral
 * @param   data        : Pointer to bytes to transmit
 * @param   numBytes    : Number of bytes to transmit
 * @return  The actual number of bytes placed into the FIFO
 * @note    This function places data into the transmit FIFO until either
 *          all the data is in the FIFO or the FIFO is full. This function
 *          will not block in the FIFO is full. The actual number of bytes
 *          placed into the FIFO is returned. This function ignores errors.
 */
int Chip_UART_Send(LPC_USART_T *pUART, const void *data, int numBytes);

/**
 * @brief   Read data through the UART peripheral (non-blocking)
 * @param   pUART       : Pointer to selected UART peripheral
 * @param   data        : Pointer to bytes array to fill
 * @param   numBytes    : Size of the passed data array
 * @return  The actual number of bytes read
 * @note    This function reads data from the receive FIFO until either
 *          all the data has been read or the passed buffer is completely full.
 *          This function will not block. This function ignores errors.
 */
int Chip_UART_Read(LPC_USART_T *pUART, void *data, int numBytes);

/**
 * @brief   Set baud rate for UART
 * @param   pUART    : The base of UART peripheral on the chip
 * @param   baseClkHz: Base clock for UART
 * @param   baudrate : Baud rate to be set
 * @return  Nothing
 */
static inline void Chip_UART_SetBaud(LPC_USART_T *pUART, uint32_t baseClkHz, uint32_t baudrate)
{
    pUART->BRG = baseClkHz / (16 * baudrate) - 1;   /* baud rate */
}

/**
 * @brief   Transmit a byte array through the UART peripheral (blocking)
 * @param   pUART       : Pointer to selected UART peripheral
 * @param   data        : Pointer to data to transmit
 * @param   numBytes    : Number of bytes to transmit
 * @return  The number of bytes transmitted
 * @note    This function will send or place all bytes into the transmit
 *          FIFO. This function will block until the last bytes are in the FIFO.
 */
int Chip_UART_SendBlocking(LPC_USART_T *pUART, const void *data, int numBytes);

/**
 * @brief   Read data through the UART peripheral (blocking)
 * @param   pUART       : Pointer to selected UART peripheral
 * @param   data        : Pointer to data array to fill
 * @param   numBytes    : Size of the passed data array
 * @return  The size of the dat array
 * @note    This function reads data from the receive FIFO until the passed
 *          buffer is completely full. The function will block until full.
 *          This function ignores errors.
 */
int Chip_UART_ReadBlocking(LPC_USART_T *pUART, void *data, int numBytes);


/*!
 * @brief 配置UART在synchorous模式的波特率分频
 *
 * UART的波特率发生器经过16倍的过采样才能得到最终的波特率，在过采样之前，由Main Clock经SYSCON的
 * UART通用分频器得到U_PCLK之后，再经各个模块的波特率发生器分频得到具体的波特率。计算公式为：
 * Baudrate = U_PCLK / (16 x BRGDIV)
 *
 * @param pUART   Pointer to selected UART peripheral
 * @param divider 对应BRGDIV的分频值, BRGDIV = divider+1
 */
static inline void Chip_UART_SetSyncBaudrateDivider(LPC_USART_T *pUART, uint32_t divider)
{
    pUART->BRG = divider;
}

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* __UART_8XX_H_ */
